
/***********************************************************************
 * Map class
 * 
 * Implements maps
 *
 * Signature:
 *  public static Map empty (int x, int y);
 *  public static Map artifact (int x, int y, Artifact a);
 *  public static Map merge (Map m1, Map m2);
 *  public Coord size ();
 *  public boolean isEmpty ();
 *  public Artifact firstArtifact ();
 *  public Map restArtifacts ();
 *  public boolean wellFormed ();
 *  public Option<Artifact> findArtifactByName (String n);
 *  public Option<Artifact> findArtifactByPosition (Coord c);
 *  public Map removeArtifact (Artifact a);
 *  public String toString ();
 *  public FuncIterator<Artifact> getFuncIterator ();
 *  public Iterator<Artifact> iterator ();
 *  public List<String> picture ();
 ***********************************************************************/


import java.util.Iterator;
import java.lang.Iterable;


public abstract class Map implements Iterable<Artifact> {

    public static Map empty (int x, int y) {
	return new MapEmpty(x,y);
    }

    public static Map artifact (int x, int y, Artifact a) {
	if (!a.position().within(Coord.create(0,0),
				 Coord.create(x-1,y-1)))
	    throw new IllegalArgumentException("Map.artifact()");
	return new MapArtifact(x,y,a);
    }

    public static Map merge (Map m1, Map m2) {
	if (!m1.size().equals(m2.size()))
	    throw new IllegalArgumentException("Map.merge()");
	Map result = new MapMerge(m1,m2);
	if (!result.wellFormed())
	    throw new IllegalArgumentException("Map.merge()");
	return result;
    }

    public abstract Coord size ();

    public abstract boolean isEmpty ();

    public abstract Artifact firstArtifact ();

    public abstract Map restArtifacts ();

    public abstract boolean wellFormed ();

    public abstract Option<Artifact> findArtifactByName (String n);

    public abstract Option<Artifact> findArtifactByPosition (Coord c);

    public abstract Map removeArtifact (Artifact a);

    public abstract String toString ();

    public abstract FuncIterator<Artifact> getFuncIterator ();

    public Iterator<Artifact> iterator () {
	return IteratorAdapter.create(this.getFuncIterator());
    }

    public List<String> picture () {
	int xmax = this.size().xPos();
	int ymax = this.size().yPos();

	if (xmax < 1 || ymax < 1)
	    throw new IllegalArgumentException("Size of drawing < 1");

	char drawing[][] = new char[xmax][ymax];

	for (int i=0; i<xmax; i++)
	    for (int j=0; j<ymax; j++)
		drawing[i][j] = '.';

	for (Artifact art : this)
	    drawing[art.position().xPos()]
		   [art.position().yPos()]=
		art.display();
	
	List<String> result = List.empty();
	for (int j=0;j<ymax; j++) {
	    char line[] = new char[xmax*2];
	    for (int i=0; i<xmax; i++)  {
		line[2*i]=' ';
		line[2*i+1]=drawing[i][j];
	    }
	    result = List.cons(new String(line),result);
	}
	return result;
    }

}


class MapEmpty extends Map {

    private int xsize;
    private int ysize;

    public MapEmpty (int x, int y) {
	xsize = x;
	ysize = y;
    }

    public Coord size () {
	return Coord.create(xsize,ysize);
    }

    public boolean isEmpty () {
	return true;
    }

    public Artifact firstArtifact () {
	throw new Error("empty().firstArtifact() undefined");
    }

    public Map restArtifacts () {
	throw new Error("empty().restArtifacts() undefined");
    }

    public boolean wellFormed () {
	return true;
    }

    public Option<Artifact> findArtifactByName (String n) {
	return Option.none();
    }

    public Option<Artifact> findArtifactByPosition (Coord c) {
	return Option.none();
    }

    public Map removeArtifact (Artifact a) {
	return this;
    }

    public String toString () {
	return "";
    }

    public FuncIterator<Artifact> getFuncIterator () {
	return new FuncIteratorEmpty();
    }

}

class FuncIteratorEmpty implements FuncIterator<Artifact> {

    public FuncIteratorEmpty () { }
	
    public boolean hasElement() {
	return false;
    }

    public Artifact current() {
        throw new java.util.NoSuchElementException ("Iterator empty");
    }

    public FuncIterator<Artifact> advance() {
        throw new java.util.NoSuchElementException ("Iterator empty");
    }
  
}



class MapArtifact extends Map {

    private int xsize;
    private int ysize;
    private Artifact artifact;

    public MapArtifact (int x, int y, Artifact a) {
	xsize = x;
	ysize = y;
	artifact = a;
    }

    public Coord size () {
	return Coord.create(xsize,ysize);
    }

    public boolean isEmpty () {
	return false;
    }

    public Artifact firstArtifact () {
	return artifact;
    }

    public Map restArtifacts () {
	return Map.empty(this.size().xPos(),this.size().yPos());
    }

    public boolean wellFormed () {
	return true;
    }

    public Option<Artifact> findArtifactByName (String n) {
	if (artifact.name().equals(n))
	    return Option.some(artifact);
	return Option.none();
    }

    public Option<Artifact> findArtifactByPosition (Coord c) {
	if (artifact.position().equals(c))
	    return Option.some(artifact);
	return Option.none();
    }

    public Map removeArtifact (Artifact a) {
	if (a==artifact)
	    return Map.empty(this.size().xPos(),
			     this.size().yPos());
	return this;
    }

    public String toString () {
	return artifact.toString();
    }

    public FuncIterator<Artifact> getFuncIterator () {
	return new FuncIteratorArtifact(artifact);
    }

}


class FuncIteratorArtifact implements FuncIterator<Artifact> {

    private Artifact artifact;

    public FuncIteratorArtifact (Artifact a) { 
	artifact = a;
    }
	
    public boolean hasElement() {
	return true;
    }

    public Artifact current() {
        return this.artifact;
    }

    public FuncIterator<Artifact> advance() {
        return new FuncIteratorEmpty();
    }
  
}


class MapMerge extends Map {

    private Map map1;
    private Map map2;

    public MapMerge (Map m1, Map m2) {
	map1 = m1;
	map2 = m2;
    }

    public Coord size () {
	return map1.size();
    }

    public boolean isEmpty () {
	return map1.isEmpty() && map2.isEmpty();
    }

    public Artifact firstArtifact () {
	if (map1.isEmpty())
	    return map2.firstArtifact();
	else 
	    return map1.firstArtifact();
    }

    public Map restArtifacts () {
	if (map1.isEmpty())
	    return map2.restArtifacts();
	else
	    return Map.merge(map1.restArtifacts(),map2);
    }

    public boolean wellFormed () {
	if (map1.isEmpty())
	    return map2.wellFormed();
	Artifact a = map1.firstArtifact();
	if (this.restArtifacts().findArtifactByPosition(a.position()).isNone()
	    && this.restArtifacts().findArtifactByName(a.name()).isNone())
	    return this.restArtifacts().wellFormed();
	return false;
    }


    public Option<Artifact> findArtifactByName (String n) {
	Option<Artifact> result;
	result = map1.findArtifactByName(n);
	if (result.isNone())
	    return map2.findArtifactByName(n);
	return result;
    }

    public Option<Artifact> findArtifactByPosition (Coord c) {
	Option<Artifact> result = map1.findArtifactByPosition(c);
	if (result.isNone())
	    return map2.findArtifactByPosition(c);
	return result;
    }

    public Map removeArtifact (Artifact a) {
	return Map.merge(map1.removeArtifact(a),
			 map2.removeArtifact(a));
    }

    public String toString () {
	return map1.toString() + " " + map2.toString();
    }

    public FuncIterator<Artifact> getFuncIterator () {
	return new FuncIteratorMerge(map1.getFuncIterator(),map2.getFuncIterator());
    }

}

class FuncIteratorMerge implements FuncIterator<Artifact> {

    private FuncIterator<Artifact> iter1;
    private FuncIterator<Artifact> iter2;

    public FuncIteratorMerge (FuncIterator<Artifact> i1, FuncIterator<Artifact> i2) {
	iter1 = i1;
	iter2 = i2;
    }
	
    public boolean hasElement() {
	return (iter1.hasElement() || iter2.hasElement());
    }

    public Artifact current() {
	if (iter1.hasElement())
	    return iter1.current();
	else
	    return iter2.current();
    }

    public FuncIterator<Artifact> advance() {
	if (iter1.hasElement())
	    return new FuncIteratorMerge(iter1.advance(),iter2);
	return iter2.advance();
    }
  
}
